import tkinter.filedialog as fd
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Random import get_random_bytes
import base64
import os
import sys
import time
import json


def updateConfig(newConfig=0):
    if newConfig != 0:
        with open("config.json", "w", encoding="utf-8") as configFile:
            json.dump(newConfig, configFile)
        return newConfig
    else:
        try:
            with open("config.json", "r+", encoding="utf-8") as configFile:
                config = json.load(configFile)
        except FileNotFoundError:
            with open("config.json", "w", encoding="utf-8") as configFile:
                json.dump({"showDisclaimer": True}, configFile)
            with open("config.json", "r+", encoding="utf-8") as configFile:
                config = json.load(configFile)
        return config


config = updateConfig()


def base64Encode(bin):
    bs64 = base64.b64encode(bin).decode("utf-8")
    # 替换非法字符
    bs64 = bs64.replace("/", "_")

    return bs64


def base64Decode(bs64):
    try:
        # 还原被替换的字符
        bs64 = bs64.replace("_", "/")
        return base64.b64decode(bs64.encode("utf-8"))
    except Exception as e:
        print(f"解密出错：{str(e)}")
        os.sys.sys.exit(1)

# 这个AES加密解密是AI写的


def aesEncrypt(text, pwd):
    """严格接收文本(str)，返回加密后的纯二进制(bytes)"""
    salt = get_random_bytes(16)
    key = PBKDF2(
        password=pwd,
        salt=salt,
        dkLen=32,
        count=100000
    )
    cipher = AES.new(key, AES.MODE_GCM)
    text_bin = text.encode("utf-8")  # 文本转二进制
    encrypted_bin, tag = cipher.encrypt_and_digest(text_bin)
    # 直接返回二进制组合（不含Base64编码）
    return salt + cipher.nonce + tag + encrypted_bin


def aesDecrypt(encrypted_bin, pwd):
    """严格接收加密二进制(bytes)，返回原始文本(str)"""
    try:
        salt = encrypted_bin[:16]
        nonce = encrypted_bin[16:32]
        tag = encrypted_bin[32:48]
        encrypted_data = encrypted_bin[48:]

        key = PBKDF2(
            password=pwd,
            salt=salt,
            dkLen=32,
            count=100000
        )
        cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
        decrypted_bin = cipher.decrypt_and_verify(encrypted_data, tag)
        return decrypted_bin.decode("utf-8")  # 二进制转文本
    except Exception as e:
        print(f"解密出错：{str(e)}")
        sys.exit(1)

# ---


def readFile(file):
    try:
        with open(file, "rb") as f:
            return f.read()
    except FileNotFoundError as e:
        print(f"读取文件出错，文件不存在：{str(e)}")
        sys.exit(1)
    except Exception as e:
        print(f"读取文件出错：{str(e)}")
        sys.exit(1)


def writeFile(file, bin):
    try:
        with open(file, "wb") as f:
            f.write(bin)
    except Exception as e:
        print(f"写入文件出错：{str(e)}")
        sys.exit(1)


print("Cyan's File Encer / Cyan的文件加密器 v0.1 by Cyan3771")
if config["showDisclaimer"]:
    for line in ["免责声明：\n⚠️重要警示\n对于商业文件、核心数据等对你具有重大价值的文件，强烈建议不要使用本软件。此类文件应通过专业加密工具或服务进行保护。",
                 "⚠️责任声明\n本软件仅为学习交流用途，开发者只是学生，没有相关法律、财务能力，所以不对使用本软件导致的任何文件丢失、损坏或信息泄露承担责任。",
                 "⚠️密码警告\n加密密码为唯一解密依据，无密码将永久无法恢复文件内容。请务必妥善保管密码，因密码遗忘或泄露导致的任何损失，开发者概不负责。",
                 "若出现任何损失，用户自愿承担因使用本软件可能导致的文件丢失、密码遗忘等全部风险。",
                 "本警告将不再显示，若要再次查看，请访问https://github.com/Cyan3771/Cyan-s-File-Encer/blob/main/README.md或者删除config.json文件。"]:

        print(line)
        for i in range(int(len(line)*0.06), 0, -1):
            print(f"{i}s后可继续。", end="")
            print(' ' * 50, end="\r")
            time.sleep(1)
        os.system("pause" if sys.platform ==
                  "win32" else 'read -n 1 -s -r -p "按任意键继续..."')
config["showDisclaimer"] = False
updateConfig(config)

mode = input("请输入加密(e)或解密(d)...")
if mode not in ["e", "d"]:
    print("无效的模式！")
    sys.exit(1)

print("请在弹窗中选择文件，若没有找到弹窗，请按Alt+Tab找名为“选择文件”的窗口...")
file = fd.askopenfilename(title="选择文件", initialdir=".")
if file == "":
    print("未选择文件！")
    sys.exit(1)
print(f"文件：{file}")

pwd = input("输入密码：")
if pwd != input("确认密码："):
    print("密码不匹配！")
    sys.exit(1)

data = readFile(file)
fileDir = os.path.normpath(os.path.dirname(file))
name = os.path.basename(file)


def enc():
    global name, data, fileDir, pwd
    # 加密文件名
    encName = base64Encode(aesEncrypt(name, pwd))
    encPath = os.path.join(fileDir, encName)

    # 加密文件内容
    encData = aesEncrypt(base64Encode(data), pwd)

    # 写文件
    if os.path.exists(encPath):
        overWrite = input(f"文件 {encName} 已存在，是(y)否(按换行键)覆盖...")
        sys.exit(0) if overWrite.lower() != "y" else None
    writeFile(encPath, encData)

    print(f"加密成功！文件路径：{encPath}")
    os.system(f'explorer /select,"{encPath}"' if sys.platform == "win32" else
              f'open -R "{encPath}"' if sys.platform == "darwin" else
              f'xdg-open "{fileDir}"')


def dec():
    global name, data, fileDir, pwd
    # 解密文件名
    decName = aesDecrypt(base64Decode(name), pwd)
    decPath = os.path.join(fileDir, decName)

    # 解密文件内容
    decData = base64Decode(aesDecrypt(data, pwd))

    # 写文件
    if os.path.exists(decPath):
        overWrite = input(f"文件 {decName} 已存在，是(y)否(按换行键)覆盖...")
        sys.exit(0) if overWrite.lower() != "y" else None
    writeFile(decPath, decData)

    print(f"解密成功！文件路径：{decPath}")
    os.system(f'explorer /select,"{decPath}"' if sys.platform == "win32" else
              f'open -R "{decPath}"' if sys.platform == "darwin" else
              f'xdg-open "{fileDir}"')
